import { defineComponent, reactive, h, ref, onMounted } from "vue";
import { ElButton, ElTabs, ElTabPane, ElMessage, ElMessageBox, ElSelect, ElOption } from "element-plus";

import ejs from "ejs";
import JsZip from "jszip";

import Blocklayout from "@/components/layout/Blocklayout.vue";
import MonacoDditor from "@/components/Document/MonacoDditor/MonacoDditor.vue";
import BaseDialog from "@/components/Common/Dialog/BaseDialog.vue";
import MonacoDditorTabs from "@/components/Common/MonacoDditorTabs";

import { message } from "@/utils/Element";
import cssModule from "./index.module.scss";
import { extractData, downloadFile } from "./util";
import mock from "./mock";
import extractExpression from "./extractExpression";
import { getExplainTemplates } from "./explainTemplate";
import explainCommonFileData from "./explainCommonFileData";

import type { Action } from "element-plus";
import type { ExprType, StateType } from "./type";

const STORE_TEMPLATE_KEY_PREFIX = "TEMPLATE-";

const EXTRACT_EXPRESSION_DATA_KEY = "EXTRACT_EXPRESSION_DATA";
const COMMON_FILE_DATA_KEY = "COMMON_FILE_DATA";

export default defineComponent({
  setup() {
    const state = reactive<StateType>({
      dataSource: JSON.stringify(mock, null, 2),
      extractExpression: "",
      extractData: "",
      commonFileData: "",
      template: [],
      tabActiveName: "dataSource",
      loading: false,
      dialogVisible: false,
      templates: [],
      templateActive: {
        oldName: "",
        name: "",
        isInput: false,
        value: ""
      },
      testFileContent: "",
      newTemplateName: ""
    });

    onMounted(() => {
      getExtractExpression();
      getCommonFileData();
    });

    /** 获取提取表达式 */
    const getExtractExpression = () => {
      const value = window.localStorage.getItem(EXTRACT_EXPRESSION_DATA_KEY);
      if (value) {
        state.extractExpression = value;
      } else {
        state.extractExpression = extractExpression;
        window.localStorage.setItem(EXTRACT_EXPRESSION_DATA_KEY, extractExpression);
      }
    };

    /** 设置提取表达式 */
    const setExtractExpression = (val: string) => {
      window.localStorage.setItem(EXTRACT_EXPRESSION_DATA_KEY, val);
    };

    /** 获取公共文件数据 */
    const getCommonFileData = () => {
      const value = window.localStorage.getItem(COMMON_FILE_DATA_KEY);
      if (value) {
        state.commonFileData = value;
      } else {
        const commonFileData = JSON.stringify(explainCommonFileData, null, 2);
        state.commonFileData = commonFileData;
        window.localStorage.setItem(COMMON_FILE_DATA_KEY, commonFileData);
      }
    };

    /** 设置公共文件数据 */
    const setCommonFileData = (val: string) => {
      window.localStorage.setItem(COMMON_FILE_DATA_KEY, val);
    };

    const openGuidance = () => {
      console.log("openGuidance");
    };

    /** 字符串转换为JSON */
    const textToJson = (text: string) => {
      let ret: any = null;
      try {
        ret = JSON.parse(text);
      } catch (err) {
        console.warn("转换失败: ", err);
      }
      return ret;
    };

    /** 提取数据 */
    const handleExtract = () => {
      try {
        const expr: ExprType = eval(`(${state.extractExpression})`);
        const dataSource = JSON.parse(state.dataSource || "[]");
        console.log("expr: ", expr);
        const { data } = extractData(dataSource, dataSource, expr);
        state.extractData = JSON.stringify(data, null, 2);
        state.tabActiveName = "extractData";
      } catch (err: unknown) {
        console.log("err: ", err);
        if (err instanceof Error) {
          message.error(err.toString());
        } else {
          message.error(String(err));
        }
      }
    };

    const testGenerateTemplate = ref("");

    /** 测试生成 */
    const testGenerate = async () => {
      if (state.templates.length === 0) {
        ElMessageBox.alert("本地没有保存的模板数据, 请先打开模板<b style='color: red'>选项卡</b>添加", "提示", {
          type: "info",
          confirmButtonText: "确定",
          dangerouslyUseHTMLString: true
        }).then((res: Action) => {
          if (res === 'confirm') {
            state.tabActiveName = "template";
          }
        });
        return;
      }

      const json: any[] = textToJson(state.extractData);
      if (!Array.isArray(json)) {
        ElMessage.warning("数据必须是数组");
        return;
      }

      if (!json[0]) {
        ElMessage.warning("最少需要一条数据");
        return;
      }

      testGenerateTemplate.value = state.templateActive.name;
      ElMessageBox({
        title: "测试生成",
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        message: () =>
          h("div", null, [
            h("span", null, "选择模板: "),
            h(
              ElSelect,
              {
                placeholder: "选择需要测试生成的模板",
                size: "small",
                style: "width: 240px",
                modelValue: testGenerateTemplate.value,
                "onUpdate:modelValue": (val: string) => {
                  testGenerateTemplate.value = val;
                }
              },
              state.templates.map((e, i) => {
                return h(ElOption, {
                  key: i,
                  label: e.name,
                  value: e.name
                });
              })
            )
          ])
      })
        .then(() => {
          const target = state.templates.find(e => e.name === testGenerateTemplate.value);
          if (target) {
            state.templateActive = target;
            const text = ejs.render(target.value, json[0]);
            state.dialogVisible = true;
            state.testFileContent = text;
          }
        })
        .catch(err => {
          console.log("err: ", err);
        });
    };

    /** 生成文件 */
    const generateFile = async () => {
      if (state.templates.length === 0) {
        ElMessageBox.alert("请先添加模板", "提示", {
          type: "info",
          confirmButtonText: "确定"
        });
        return;
      }

      try {
        const { value: fileName } = await ElMessageBox.prompt("请输入生成的文件名", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          inputPattern: /\S/,
          inputErrorMessage: "文件名不能为空"
        });

        let json: any[] = JSON.parse(state.extractData);
        if (!Array.isArray(json)) json = [json];
        const time = Date.now();
        const zip = new JsZip();
        for (const item of json) {
          if ("$mate" in item && Array.isArray(item.$mate)) {
            for (const info of item.$mate) {
              if ("template" in info || "filePath" in info) {
                const target = state.templates.find(e => e.name === info.template);
                let localStorageVal = "";
                if (target) {
                  localStorageVal = target.value;
                } else {
                  console.warn(`${info.template} 对应的模板数据为空`);
                }
                const text = ejs.render(localStorageVal, item);
                zip.file(info.filePath, text);
              } else {
                console.error(`meta 数据缺少【template】或者【filePath】字段`, info);
              }
            }
          } else {
            console.error(`数据缺少【$mate】字段`, item);
          }
        }

        // 添加公共文件
        if (state.commonFileData) {
          const commonFileData: {
            template?: string;
            filePath?: string;
          }[] = textToJson(state.commonFileData);

          commonFileData.forEach(e => {
            if (e.filePath && e.template) {
              const target = state.templates.find(item => item.name === e.template);
              if (target) {
                zip.file(e.filePath, target.value);
              } else {
                console.warn(`${e.template} 对应的模板数据为空`);
              }
            }
          });
        }

        const content = await zip.generateAsync({ type: "blob" });
        downloadFile(content, fileName + ".zip");
        console.log("共用时: %sms", Date.now() - time);
      } catch (err) {
        message.error("生成失败了: " + err);
        ElMessage.warning(String(err));
      }
    };

    /** 数据源插槽内容 */
    const dataSourceSlots = {
      left: () => <MonacoDditor v-model={state.dataSource} language="json" />,
      menu: () => (
        <div class={cssModule["menu"]}>
          <ElButton type="primary" size="small" loading={state.loading} onClick={openGuidance}>
            说 明
            <i class="iconfont icon-fuzhi" />
          </ElButton>
          <ElButton type="primary" size="small" loading={state.loading} onClick={handleExtract}>
            提 取
            <i class="iconfont icon-fuzhi" />
          </ElButton>
        </div>
      ),
      right: () => (
        <MonacoDditor
          v-model={state.extractExpression}
          language="javascript"
          onChange={e => {
            setExtractExpression(e);
          }}
        />
      )
    };

    return () => (
      <div class={cssModule.container}>
        <ElTabs v-model={state.tabActiveName} style={{ width: "100%" }}>
          <ElTabPane label="数据源" name="dataSource" />
          <ElTabPane label="模板" name="template" />
          <ElTabPane label="公共文件" name="commonFileData" />
          {state.extractData && <ElTabPane label="提取的数据" name="extractData" />}
        </ElTabs>

        {state.tabActiveName === "dataSource" && <Blocklayout class={cssModule["full"]} v-slots={dataSourceSlots} />}
        {state.tabActiveName === "extractData" && (
          <div class={cssModule["full"]}>
            <ElButton type="primary" onClick={testGenerate}>
              测试生成
            </ElButton>
            <ElButton type="primary" onClick={generateFile}>
              生成文件
            </ElButton>
            <MonacoDditor style={{ marginTop: "10px" }} v-model={state.extractData} language="json" />
          </div>
        )}
        {state.tabActiveName === "commonFileData" && (
          <MonacoDditor
            v-model={state.commonFileData}
            language="json"
            onChange={e => {
              setCommonFileData(e);
            }}
          />
        )}
        {state.tabActiveName === "template" && (
          <MonacoDditorTabs
            v-model={state.templates}
            v-model:templateActive={state.templateActive}
            templateKeyPrefix={STORE_TEMPLATE_KEY_PREFIX}
            defaultTemplates={getExplainTemplates()}
          />
        )}

        <BaseDialog
          v-model={state.dialogVisible}
          title="测试生成的文件内容"
          width="70vw"
          v-slots={{
            default: () => <MonacoDditor v-model={state.testFileContent} />
          }}
        />
      </div>
    );
  }
});
